#include <unistd.h>
#include <cstdlib>
#include <cstring>
#include <netdb.h>
#include <iostream>
#include <udt.h>
#include "cc.h"
#include "test_util.h"

using namespace std;
void* monitor(void*);

int set_udtp_socketopt(UDTSOCKET udt_sock)
{
    int val, mss;
    struct linger udtlinger;
#if 0
	int64_t maxbw;
//	val = 64;// mininum is 32, little than 32 is invalid
//	UDT::setsockopt(udt_sock,0,UDT_FC,(char*)&val,sizeof(int));

	//maxbw = 64*1024/8;// bytes per second, Non-blocking call-failure
	maxbw = 2048*1024/8;// bytes per second
	UDT::setsockopt(udt_sock,0,UDT_MAXBW,(char*)&maxbw,sizeof(int64_t));
#endif 
    /* udp mss 大小 */
    mss = 1052;
    UDT::setsockopt(udt_sock, 0,
            UDT_MSS, (char *)&mss, sizeof(int));
    /* udt发送buff大小 */
	mss -= 28;
    val = 160 * mss; // 160KB
    UDT::setsockopt(udt_sock, 0, 
			UDT_SNDBUF, (char *)&val, sizeof(int));
    /* udt接收buff大小 */
    val = 64 * mss; // 64KB
    UDT::setsockopt(udt_sock, 0,
            UDT_RCVBUF, (char *)&val, sizeof(int));
    /* udp 发送buff大小 */
    val = 8 << 10;
    UDT::setsockopt(udt_sock, 0,
            UDP_SNDBUF, (char *)&val, sizeof(int));
    /* udp 接收buff大小 */
    val = 16 << 10;
    UDT::setsockopt(udt_sock, 0,
            UDP_RCVBUF, (char *)&val, sizeof(int));
    /* 阻塞方式发送 */
    val = 1;
    UDT::setsockopt(udt_sock, 0,
            UDT_SNDSYN, (char *)&val, sizeof(int));

#if 1
    /* 阻塞方式接收 */
    bool rcval = true;
    UDT::setsockopt(udt_sock, 0,
            UDT_RCVSYN, (char *)&rcval, sizeof(bool));
#endif
    /* 超时值10秒 */
    val = 10000;
    UDT::setsockopt(udt_sock, 0,
			UDT_SNDTIMEO, (char *)&val, sizeof(int));
    val = 10000;
    UDT::setsockopt(udt_sock, 0,
			UDT_RCVTIMEO, (char *)&val, sizeof(int));

    /* socket close后一秒处理socket buffer，udt_close处理阻塞时间为2秒 
	 * 当用密码错误时，如果udt_close不阻塞，那错误密码信息不会发到用户那里*/
    udtlinger.l_onoff = 1;
    udtlinger.l_linger = 2;
    UDT::setsockopt(udt_sock,0,UDT_LINGER,(char *)&udtlinger, sizeof(udtlinger));
	return 0;
}

int main(int argc, char* argv[])
{
   if ((3 != argc) || (0 == atoi(argv[2])))
   {
      cout << "usage: " << argv[0] << " server_ip server_port" << endl;
      return 0;
   }

   // Automatically start up and clean up UDT module.
   UDTUpDown _udt_;
   //bool connt_status = false;
   struct addrinfo hints, *local, *peer;

   memset(&hints, 0, sizeof(struct addrinfo));

   hints.ai_flags = AI_PASSIVE;
   hints.ai_family = AF_INET;
   //hints.ai_socktype = SOCK_STREAM;
   hints.ai_socktype = SOCK_DGRAM;

   if (0 != getaddrinfo(NULL, "9000", &hints, &local))
   {
      cout << "incorrect network address.\n" << endl;
      return 0;
   }

   if (0 != getaddrinfo(argv[1], argv[2], &hints, &peer))
   {
      cout << "incorrect server/peer address. " << argv[1] << ":" << argv[2] << endl;
      return 0;
   }

  // pthread_t monitor_tid;
  // ret = pthread_create(&monitor_tid, NULL, monitor, &client);
  
    int user = 0;
	int size = 5*1024;
	char* data = new char[size];
	int ret = 0;
	time_t o_time,n_time;

   while(1)
   {
	   UDTSOCKET client = UDT::socket(local->ai_family, local->ai_socktype, local->ai_protocol);
	   set_udtp_socketopt(client);

		// connect to the server, implict bind
		if (UDT::ERROR == UDT::connect(client, peer->ai_addr, peer->ai_addrlen))
		{
		  cout << "connect: " << UDT::getlasterror().getErrorMessage() << endl;
		  break;
		}

		sprintf(data,"PC Client Login------%d",user++);
		ret = UDT::sendmsg(client, data, strlen(data)+1, 1, 1);
		if(UDT::ERROR == ret)
		{
		  cout << "send:" << UDT::getlasterror().getErrorMessage() 
			<< "errcode = "<< UDT::getlasterror().getErrorCode() << endl;
			UDT::close(client);
			continue;
		}

		o_time = time(NULL);
		n_time = o_time;
		int recv_tot = 0;
		while(n_time < o_time+2)
		{// recv  data
			ret = UDT::recvmsg(client,data,size);
			if(UDT::ERROR == ret)
			{
			  cout << "recv:" << UDT::getlasterror().getErrorMessage() 
				<< "errcode = "<< UDT::getlasterror().getErrorCode() << endl;
			  break;
			}
			recv_tot += ret;
			//cout << "recv data len = " << ret << endl;
			n_time = time(NULL);
		}
		cout << "recv data total len = " << recv_tot 
			<< ",speed = " << recv_tot*8/2<< "(kbps)" 
			<< ",now time = " << n_time << endl;
		cout << endl;
		UDT::close(client);
		sleep(1);
   }

   freeaddrinfo(local);
   freeaddrinfo(peer);
	delete [] data;
  // sleep(7);
  // pthread_cancel(monitor_tid);
  
   return 0;
}

void* monitor(void* s)
{
   UDTSOCKET u = *(UDTSOCKET*)s;

   UDT::TRACEINFO perf;

   while (true)
   {
      sleep(1);
      if (UDT::ERROR == UDT::perfmon(u, &perf))
      {
         cout << "perfmon: " << UDT::getlasterror().getErrorMessage() << endl;
         break;
      }
#if 0
	  cout << "------------------------Send Statistic-------------------------" << endl;
	  cout << "pktSndTot|pktLossTot|pktResndTot|PktSndPeriod(us)|SendRate(Mb/s)|RTT(ms)|FWnd|CWnd|FlightSize|AvailSndBuf(Bytes)" << endl;
      cout << perf.pktSentTotal << "\t"
		   << perf.pktSndLossTotal << "\t"
		   << perf.pktRetransTotal << "\t"
           << perf.usPktSndPeriod << "\t" 
		   << perf.mbpsSendRate << "\t" 
           << perf.msRTT << "\t" 
           << perf.pktFlowWindow << "\t" 
           << perf.pktCongestionWindow << "\t" 
           << perf.pktFlightSize << "\t" 
	  	   << perf.byteAvailSndBuf << endl;

	  cout << "------------------------Recv Statistic-------------------------" << endl;
	  cout << "pktRcvTot|pktLossTot|pktRcvAckTot|PktRcvNAKTot|RcvAck|RcvNAK|RecvRate(Mb/s)|BandWidth(Mbps)|AvailRcvBuf(Bytes)" << endl;
	  cout << perf.pktRecvTotal << "\t"
		  << perf.pktRcvLossTotal << "\t"
		  << perf.pktRecvACKTotal << "\t"
		  << perf.pktRecvNAKTotal << "\t"
		  << perf.pktRecvACK << "\t" 
          << perf.pktRecvNAK << "\t"
		  << perf.mbpsRecvRate << "\t"
		  << perf.mbpsBandwidth << "\t"
		  << perf.byteAvailRcvBuf << endl;
#endif

	  cout << "------------------------Send Statistic-------------------------" << endl;
	  cout << "pktSndTot|pktLossTot|pktRetransTot|SendRate(Mb/s)|RTT(ms)|BandWidth(Mbps)" << endl;
      cout << perf.pktSentTotal << "\t"
		   << perf.pktSndLossTotal << "\t"
		   << perf.pktRetransTotal << "\t"
		   << perf.mbpsSendRate << "\t" 
           << perf.msRTT << "\t" 
		   << perf.mbpsBandwidth << endl; 
   }

   return NULL;
}
